iT邦幫忙

2022 iThome 鐵人賽

DAY 11
0
自我挑戰組

學習JavaScript的基礎概念系列 第 11

Day11 閉包closure (二)

  • 分享至 

  • xImage
  •  

在常運用閉包是在處理回呼或計時器的時候,需要在某個不確定的時間點、以非同步方式呼叫某函式。
若沒有閉包機制,想要在同一時間執行多個動作,不論是事件處理、動畫、Ajax請求,將會非常困難;閉包不只是把建立時的值拍照紀錄而已,而是在狀態封裝後仍繼續活著,只要閉包存在,就可以修改裡頭的狀態。

閉包經典例子:

function buildFunctions(){
    var arr = [];
    //for迴圈
    for (var i=0 ; i<3 ; i++){
        arr.push( //在arr陣列裡放入3個function
            function(){ 
                console.log(i);
            });//console.log不會在這時被執行,只是創造物件,因為還沒被呼叫
    }
    return arr;
}

var fs = buildFunctions();

fs[0]();
fs[1]();
fs[2]();

執行結果
https://imgur.com/lcoW4Qn.jpg
for迴圈執行第一次時i為1,第二次i為2,第三次i為3

原因:
因為i++最先執行,最後i為2時,i++又執行了一次i為3,然後判斷i<3的表示式,發現i不小於3時才離開了迴圈,這時i離開的值是3,所以記憶體中i停留的值為3,而不是我們創造函數時候的值,所以三個輸出都一樣是3,因為三個都指向同一個記憶體位置。

console.log不是在它所在的地方執行,而是當我們呼叫函數時執行,i的值是在執行函數時後的值(還沒呼叫)

讓執行結果是012方法

方法一:
使用ES6的let

function buildFunctions22(){ 
    var arr = [];
    //for迴圈
    for (var i=0 ; i<3 ; i++){
        let j = i; //用let宣告,每次for迴圈執行時,這會是記憶體中一個新的變數,會放在不同的記憶體位置。

        arr.push( 
            function(){ 
                console.log(j); 
            });
    }
    return arr;
}

var fs2 = buildFunctions2(); 

fs2[0](); 
fs2[1](); 
fs2[2]();

方法二:
IIFE立刻執行函數
獲得執行環境唯一的方法就是執行函數

function buildFunctions22(){ 
    var arr = [];
    //for迴圈
    for (var i=0 ; i<3 ; i++){


        arr.push( 
            (function(j){
                return function(){
                    console,log(j);
                }
            }(i))

        )
    }
    return arr;
}

var fs2 = buildFunctions2(); 

fs2[0](); 
fs2[1](); 
fs2[2]();

每執行新的一行,每次執行都創造了自己的執行環境,j會被存在這三個執行環境。
j它不需要到迴圈去找,只要到自己的這個執行環境就好,j的值會在迴圈執行時儲存。


上一篇
Day10 閉包closure (一)
下一篇
Day12 立即呼叫的函數表示式(IIFE)
系列文
學習JavaScript的基礎概念30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言